/*
 * wavefile.cpp
 * This file is part of trypsyndsp
 *
 * Copyright (C) 2012 - Luke William Westby
 *
 * trypsyndsp is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * trypsyndsp is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with trypsyndsp. If not, see <http://www.gnu.org/licenses/>.
 */

#include "wavefile.h"
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <iostream>
#include <stdint.h>
#include <string>
#include <iomanip>

using namespace std;

/* -------------------------------------------------------------------------- *
 * empty constructor
 * -------------------------------------------------------------------------- */

WaveFile::WaveFile() {
	
	o_buf = NULL;
	u_buf_UI8 = NULL;
	u_buf_SI16 = NULL;
	u_buf_SI32 = NULL;
	u_buf_SF32 = NULL;
	
	WaveClear();
}

/* -------------------------------------------------------------------------- *
 * filename constructor
 * -------------------------------------------------------------------------- */

WaveFile::WaveFile(char *fn) {
	o_buf = NULL;
	u_buf_UI8 = NULL;
	u_buf_SI16 = NULL;
	u_buf_SI32 = NULL;
	u_buf_SF32 = NULL;
	
	WaveOpen(fn);
}

/* -------------------------------------------------------------------------- *
 * file descriptor constructor
 * -------------------------------------------------------------------------- */

WaveFile::WaveFile(int fdes) {
	
	o_buf = NULL;
	u_buf_UI8 = NULL;
	u_buf_SI16 = NULL;
	u_buf_SI32 = NULL;
	u_buf_SF32 = NULL;
	
	WaveOpen(fdes);
}

/* -------------------------------------------------------------------------- *
 * destructor
 * -------------------------------------------------------------------------- */

WaveFile::~WaveFile() {
	
	WaveClear();
}

/* -------------------------------------------------------------------------- *
 * clear all data
 * -------------------------------------------------------------------------- */
 
void WaveFile::WaveClear() {

	// clear all the heap memory
	if(o_buf != NULL) delete o_buf;
	if(u_buf_UI8 != NULL) delete u_buf_UI8;
	if(u_buf_SI16 != NULL) delete u_buf_SI16;
	if(u_buf_SI32 != NULL) delete u_buf_SI32;
	if(u_buf_SF32 != NULL) delete u_buf_SF32;
	// don't delete filename, it probably isn't on the heap

	// set id placeholders to 0
	memset(ChunkId, 0, 4);
	memset(Format, 0, 4);
	memset(SubChunk1Id, 0, 4);
	memset(SubChunk2Id, 0, 4);
	
	// zero out
	filename = NULL;
	fd = open("/dev/null", O_RDONLY);
	ChunkSize = 0;
	SubChunk1Size = 0;
	AudioFormat = 0;
	NumChannels = 0;
	SampleRate = 0;
	ByteRate = 0;
	BlockAlign = 0;
	BitsPerSample = 0;
	SubChunk2Size = 0;
	o_buf = NULL;
	u_buf_UI8 = NULL;
	u_buf_SI16 = NULL;
	u_buf_SI32 = NULL;
	u_buf_SF32 = NULL;
}

/* -------------------------------------------------------------------------- *
 * filename open
 * -------------------------------------------------------------------------- */

int WaveFile::WaveOpen(char *fn) {
	
	// clear everything
	WaveClear();
	
	// grab filename
	filename = fn;
	
	// open filename
	FILE *fp = fopen(filename, "rb");
	
	// read header
	fread(ChunkId, 1, 4, fp);
	fread(&ChunkSize, 4, 1, fp);
	fread(Format, 1, 4, fp);
	fread(SubChunk1Id, 1, 4, fp);
	fread(&SubChunk1Size, 4, 1, fp);
	fread(&AudioFormat, 2, 1, fp);
	fread(&NumChannels, 2, 1, fp);
	fread(&SampleRate, 4, 1, fp);
	fread(&ByteRate, 4, 1, fp);
	fread(&BlockAlign, 2, 1, fp);
	fread(&BitsPerSample, 2, 1, fp);
	fread(SubChunk2Id, 1, 4, fp);
	fread(&SubChunk2Size, 4, 1, fp);
	
	// allocate audio buffer
	o_buf = new uint8_t [SubChunk2Size];
	
	// copy data into buffer
	fread(o_buf, 1, SubChunk2Size, fp);
	
	// close file
	fclose(fp);

	Format[5] = 0;

	return 0;
}

/* -------------------------------------------------------------------------- *
 * open from a file descriptor
 * -------------------------------------------------------------------------- */

int WaveFile::WaveOpen(int fdes) {
	
	// clear everything
	WaveClear();
	
	// grab descriptor (fdes is already opened by definition)
	fd = fdes;
		
	// read header
	read(fd, ChunkId, 4);
	read(fd, &ChunkSize, 4);
	read(fd, Format, 4);
	read(fd, SubChunk1Id, 4);
	read(fd, &SubChunk1Size, 4);
	read(fd, &AudioFormat, 2);
	read(fd, &NumChannels, 2);
	read(fd, &SampleRate, 4);
	read(fd, &ByteRate, 4);
	read(fd, &BlockAlign, 2);
	read(fd, &BitsPerSample, 2);
	read(fd, SubChunk2Id, 4);
	read(fd, &SubChunk2Size, 4);
	
	// allocate audio buffer
	o_buf = new uint8_t [SubChunk2Size];
	
	// copy data into buffer
	read(fd, o_buf, SubChunk2Size);
	
	// close file
	close(fd);
	
	return 0;
}

/* -------------------------------------------------------------------------- *
 * print debug data to the terminal
 * -------------------------------------------------------------------------- */
 
void WaveFile::WavePrint() {
	
	if(filename) cout << "Filename: " << filename << endl;
	else cout << "File Descriptor: " << fd << endl;
	cout << "Chunk ID: " << ChunkId << endl;
	cout << "Chunk Size: " << ChunkSize << endl;
	cout <<	"Format: " << setw(4) << string(Format) << endl;
	cout << "SubChunk1Id: " << setw(4) << string(SubChunk1Id) << endl;
	cout << "SubChunk1Size: " << SubChunk1Size << " bytes" << endl;
	cout <<	"AudioFormat: " << AudioFormat << endl;
	cout << "NumChannels: " << NumChannels << " channels" << endl;
	cout << "SampleRate: " << SampleRate << " samples/sec" << endl;
	cout << "ByteRate: " << ByteRate << " bytes/sec" << endl;
	cout << "BlockAlign: " << BlockAlign << " bytes/frame" << endl;
	cout << "BitsPerSample: " << BitsPerSample << "bits/sample" << endl;
	cout << "SubChunk2Id: " << SubChunk2Id << endl;
	cout <<	"SubChunk2Size: " << SubChunk2Size << " bytes" << endl;
	
	fflush(stdout);
}
